# Shop.gd  (Godot 4.4)
extends Window

signal buy_item(item_type:int, amount:int, price:int)
signal buy_weapon(equip:Dictionary, price:int)
signal buy_armor(equip:Dictionary, price:int)
signal shop_closed()

@onready var _title  : Label          = $VBox/Title
@onready var _gold_l : Label          = $VBox/Gold
@onready var _offers : VBoxContainer  = $VBox/Offers
@onready var _close  : Button         = $VBox/BtnClose
@onready var DBRef := DB 
@onready var _tip      : Panel          = $VBox/Tip
@onready var _tip_body : RichTextLabel  = $VBox/Tip/TipBody

var _cur_weapon_ref: Dictionary = {}
var _cur_armor_ref : Dictionary = {}

var _wave:int = 1
var _gold:int = 0
const RAR_COLORS = {0: Color(0.85,0.85,0.85), 1: Color(0.45,0.75,1.0), 2: Color(0.75,0.45,1.0)}
const ICON_CRIT = "★"     # 暴击
const ICON_BURN = "🔥"     # 燃烧
const ICON_LS   = "🩸"     # 吸血
var _cfg_items: Dictionary = {}
var _cfg_rules: Dictionary = {}
const RAR_MAP := {0:"common", 1:"rare", 2:"epic"}

func _rar_hex(c: Color) -> String:
	var r := int(clamp(c.r * 255.0, 0.0, 255.0))
	var g := int(clamp(c.g * 255.0, 0.0, 255.0))
	var b := int(clamp(c.b * 255.0, 0.0, 255.0))
	return "#%02X%02X%02X" % [r, g, b]

func _delta_line(label:String, delta:int) -> String:
	if delta > 0:
		return "[color=#3CCB77]%s %+d[/color]\n" % [label, delta]
	elif delta < 0:
		return "[color=#E05A5A]%s %+d[/color]\n" % [label, delta]
	else:
		return "[color=#9AA3AB]%s %+d[/color]\n" % [label, delta]

func _affix_lines(e:Dictionary) -> String:
	var s := ""
	var v:int = int(e.get("crit",0))
	if v > 0: s += "★ 暴击 +%d%%\n" % v
	v = int(e.get("burn",0))
	if v > 0: s += "🔥 燃烧 +%d%%\n" % v
	v = int(e.get("lifesteal",0))
	if v > 0: s += "🩸 吸血 +%d%%\n" % v
	return s
func _on_hover_weapon(cand:Dictionary):
	var text := _equip_compare_bbcode("weapon", _cur_weapon_ref, cand)
	_show_tip(get_viewport().get_mouse_position(), text)

func _on_hover_armor(cand:Dictionary):
	var text := _equip_compare_bbcode("armor", _cur_armor_ref, cand)
	_show_tip(get_viewport().get_mouse_position(), text)

func _equip_compare_bbcode(slot:String, current:Dictionary, cand:Dictionary) -> String:
	var head_col = RAR_COLORS.get(int(cand.get("rarity",0)), Color(1,1,1))
	var head := "[b][color=%s]%s[/color][/b]\n" % [_rar_hex(head_col), String(cand.get("name",""))]
	var d_atk:int = int(cand.get("atk_bonus",0)) - int(current.get("atk_bonus",0))
	var d_def:int = int(cand.get("def_bonus",0)) - int(current.get("def_bonus",0))
	var body := _delta_line("ATK", d_atk) + _delta_line("DEF", d_def)
	var aff := _affix_lines(cand)
	if aff != "":
		body += "\n" + aff
	return head + body

func _show_tip(_global_pos: Vector2, text: String):
	_tip_body.clear()
	_tip_body.append_text(text)
	_tip.visible = true
	var root: Control = _tip.get_parent() as Control   # Window 内的 VBox 容器
	var pos := root.get_local_mouse_position() + Vector2(16, 16)
	var size := Vector2(
		max(320.0, _tip_body.get_content_width() + 24.0),
		min(480.0, _tip_body.get_content_height() + 16.0)
	)
	_tip.size = size
	var root_size := root.size
	if pos.x + size.x > root_size.x:
		pos.x = root_size.x - size.x - 8.0
	if pos.y + size.y > root_size.y:
		pos.y = root_size.y - size.y - 8.0
	_tip.position = pos


func _hide_tip():
	_tip.visible = false

func _load_json(path:String) -> Dictionary:
	if not ResourceLoader.exists(path): return {}
	var s := FileAccess.get_file_as_string(path)
	var d = JSON.parse_string(s)
	if typeof(d) == TYPE_DICTIONARY: return d
	return {}

func _ready():
	#_cfg_items = _load_json("res://data/items.json")
	#_cfg_rules = _load_json("res://data/equipment_rules.json")
	size = Vector2i(900, 600)
	min_size = Vector2i(720, 480)
	close_requested.connect(func(): hide())   # 点窗口右上角关闭
	_close.pressed.connect(func():
		hide()
		emit_signal("shop_closed")
	)
	DBRef.reloaded.connect(func():
		if visible:
			_rebuild_offers()
			update_gold(_gold)
		)

# 之前：open_shop(wave:int, gold:int)
# 现在：
func open_shop(wave:int, gold:int, cur_weapon:Dictionary, cur_armor:Dictionary):
	_wave = wave
	_gold = gold
	_cur_weapon_ref = cur_weapon
	_cur_armor_ref = cur_armor
	_title.text = "商店（第 %d 波后）" % wave
	_gold_l.text = "金币：%d" % _gold
	_rebuild_offers()
	size = Vector2i(900, 600)
	popup_centered(size)



func update_gold(g:int):
	_gold = g
	_gold_l.text = "金币：%d" % _gold

func _clear(node:Node):
	for c in node.get_children(): c.queue_free()

func _rebuild_offers():
	_clear(_offers)
	var shop_items:Array = DBRef.items.get("shop", [])
	for e in shop_items:
		_add_item_offer("%s(+%d)" % [String(e.get("name","")), int(e.get("amount",0))],
			int(e.get("price",0)), int(e.get("type",0)), int(e.get("amount",0)))
	var W = _roll_weapon(_wave)     # 仍用你现有的函数，但把内部改用 DBRef.equip_rules
	var A = _roll_armor(_wave)
	var price_cfg:Dictionary = DBRef.equip_rules.get("price", {"base":60,"weapon_wave":15,"armor_wave":12})
	var price_w:int = int(price_cfg.get("base",60)) * (int(W["rarity"])+1) + _wave * int(price_cfg.get("weapon_wave",15))
	var price_a:int = int(price_cfg.get("base",60)) * (int(A["rarity"])+1) + _wave * int(price_cfg.get("armor_wave",12))
		

					


func _add_item_offer(label:String, price:int, item_type:int, amount:int):
	var b := Button.new()
	b.text = "购买：%s  价格:%d" % [label, price]
	b.pressed.connect(func():
		emit_signal("buy_item", item_type, amount, price)
	)
	_offers.add_child(b)

func _add_weapon_offer(W:Dictionary, price:int):
	var aff := []
	if int(W["crit"]) > 0: aff.append("%s+%d%%" % [ICON_CRIT, int(W["crit"])])
	if int(W["burn"]) > 0: aff.append("%s+%d%%" % [ICON_BURN, int(W["burn"])])
	if int(W["lifesteal"]) > 0: aff.append("%s+%d%%" % [ICON_LS, int(W["lifesteal"])])

	var b := Button.new()
	b.text = "购买武器 %s  价格:%d  ATK%+d DEF%+d  {%s}" % [
		String(W["name"]), price, int(W["atk_bonus"]), int(W["def_bonus"]),
		", ".join(aff)
	]
	b.add_theme_color_override("font_color", RAR_COLORS.get(int(W["rarity"]), Color.WHITE))
	b.pressed.connect(func():
		emit_signal("buy_weapon", W, price)
	)
	_offers.add_child(b)
	b.mouse_entered.connect(Callable(self, "_on_hover_weapon").bind(W))
	b.mouse_exited.connect(func(): _hide_tip())



func _add_armor_offer(A:Dictionary, price:int):
	var b := Button.new()
	b.text = "购买护甲 %s  价格:%d  ATK%+d DEF%+d" % [
		String(A["name"]), price, int(A["atk_bonus"]), int(A["def_bonus"])
	]
	b.add_theme_color_override("font_color", RAR_COLORS.get(int(A["rarity"]), Color.WHITE))
	b.pressed.connect(func():
		emit_signal("buy_armor", A, price)
	)
	_offers.add_child(b)
	b.pressed.connect(func():
		emit_signal("buy_armor", A, price)
	)
	b.mouse_entered.connect(Callable(self, "_on_hover_armor").bind(A))
	b.mouse_exited.connect(func(): _hide_tip())



# ---------- 随机生成（与 raylib 版一致的风格） ----------
# rarity: 0=普通,1=稀有,2=史诗
func _rarity_name(r:int)->String:
	return ["普通","稀有","史诗"][r]

func _weighted_rarity() -> int:
	var w:Dictionary = DBRef.equip_rules.get("rarity_weights", {"common":50,"rare":35,"epic":15})
	var entries := [["common", int(w.get("common",50))],
					["rare",   int(w.get("rare",35))],
					["epic",   int(w.get("epic",15))]]
	var total := 0
	for x in entries: total += x[1]
	var r = randi() % max(total,1)
	var acc := 0
	for x in entries:
		acc += x[1]
		if r < acc:
			if x[0] == "common": return 0
			if x[0] == "rare":   return 1
			return 2
	return 0

func _tier_name(value:int, tiers:Array, key:String) -> String:
	for t in tiers:
		if value < int(t.get(key, 99999)):
			return String(t.get("name",""))
	#return String(tiers.back().get("name",""))
	return "What fuck!"

func _roll_weapon(wave:int)->Dictionary:
	var rar := _weighted_rarity()
	var atk_base:int = 3 + wave + RandomNumberGenerator.new().randf_range(2,10)
	var def_base:int = RandomNumberGenerator.new().randf_range(2,10)
	var w := {"name":"", "slot":0, "atk_bonus":atk_base, "def_bonus":def_base,
			  "equipped": true, "crit":0, "burn":0, "lifesteal":0, "rarity":rar}
	var aff = _cfg_rules.get("affix_values", {})
	if rar == 0:
		if (randi()%100) < 40:
			var p := (randi()%3)+1
			if p==1: w["crit"]      = int(aff.get("crit",{}).get("common",8))
			if p==2: w["burn"]      = int(aff.get("burn",{}).get("common",15))
			if p==3: w["lifesteal"] = int(aff.get("lifesteal",{}).get("common",8))
	elif rar == 1:
		var p := (randi()%3)+1
		if p==1: w["crit"]      = int(aff.get("crit",{}).get("rare",15))
		if p==2: w["burn"]      = int(aff.get("burn",{}).get("rare",30))
		if p==3: w["lifesteal"] = int(aff.get("lifesteal",{}).get("rare",15))
	else:
		var p1 := (randi()%3)+1
		var p2 := (randi()%3)+1
		while p2==p1: p2 = (randi()%3)+1
		if p1==1 or p2==1: w["crit"]      = int(aff.get("crit",{}).get("epic",20))
		if p1==2 or p2==2: w["burn"]      = int(aff.get("burn",{}).get("epic",40))
		if p1==3 or p2==3: w["lifesteal"] = int(aff.get("lifesteal",{}).get("epic",25))

	var tiers:Array = _cfg_rules.get("weapon_tiers", [])
	var base_name := _tier_name(atk_base, tiers, "atk_threshold")
	w["name"] = "%s[%s]" % [base_name, ["普通","稀有","史诗"][rar]]
	return w

func _roll_armor(wave:int)->Dictionary:
	var rar := _weighted_rarity()
	
	var def_base:int = 2 + wave + RandomNumberGenerator.new().randf_range(2,10)
	var A := {"name":"", "slot":1, "atk_bonus":0, "def_bonus":def_base, "equipped": true, "rarity":rar}
	var tiers:Array = _cfg_rules.get("armor_tiers", [])
	var base_name := _tier_name(def_base, tiers, "def_threshold")
	A["name"] = "%s[%s]" % [base_name, ["普通","稀有","史诗"][rar]]
	return A
